/** @file    SystemServicesBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SystemServicesBundle.cpp 2887 2010-08-11 08:09:41Z alex $
 *  @brief
 * File containing methods for the wosh::services::SystemServicesBundle class.
 * The header for this class can be found in SystemServicesBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "SystemServicesBundle.h"

 #include "SystemHost_SerializationXML.h"

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/Fact.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/MessageFilter.h>
 #include <core/PersistenceManager.h>
 #include <core/SystemError.h>
 #include <framework/network/NetworkUtilities.h>

 
// from wakeonlan.cpp
WRESULT do_wakeonlan( const std::string& address_port, const std::string& mac_address );
void mac2string( const char* MAC, std::string& mac_string );
void string2mac( const std::string& mac_string, char* MAC );
char* generateMagicPacketFor( const char* MAC );


using namespace std;
namespace wosh {
 using namespace network;
 namespace services {

 WOSH_REGISTER(wosh::services::SystemServicesBundle, "wosh::interfaces::services::SystemServices", _SystemServices_VERSION, _static_SystemServices1 )
 WOSH_REGISTER(wosh::services::SystemServicesBundle, "wosh::BundleGeneric", _SystemServices_VERSION, _static_SystemServices2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SystemServicesBundle::SystemServicesBundle()
	: BundleGeneric() {
	BundleGeneric::setName( _SystemServices_NAME, false );
	Properties.update( _KEY_Version, _SystemServices_VERSION );

//Log.setLevel(LOG_VERBOSE);

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _SystemServices_KEY_count,	0,	Permission(Permission::Read) );

	Log(LOG_DEBUG, " Registering methods.." );
	MethodList* mmListSystemHosts = Methods.create<MethodList>( _METHOD_List, "List SystemHosts", Permission::R_XR_XR__() );
	mmListSystemHosts->setDataFieldExtractor( DataFieldExtractor<SystemHost>::createFor< DataAdapterMapT<string,SystemHost> >(this->objects, "SystemHosts") );

	MethodRetrieve* mmRetrieveSystemHosts = Methods.create<MethodRetrieve>( _METHOD_Retrieve, "Retrieve SystemHost(s)", Permission::R_XR_XR__() );
	mmRetrieveSystemHosts->setItemExtractor( ObjectExtractor<SystemHost, long>::createFor< DataAdapterMapT<string,SystemHost> >(this->objects, &SystemHost::getID ) );

	MethodWRESULT* mmLoadObjects = Methods.create<MethodWRESULT>( _SystemServices_METHOD_load, "Load from DB", Permission::R_XR__R__() );
	mmLoadObjects->setMethod( this, (MethodWRESULTPtr)&SystemServicesBundle::loadObjects );
	MethodWRESULT* mmSaveObjects = Methods.create<MethodWRESULT>( _SystemServices_METHOD_save, "Save to DB", Permission::R_XR_XR__() );
	mmSaveObjects->setMethod( this, (MethodWRESULTPtr)&SystemServicesBundle::saveObjects );

	MethodMessageResponse* mmWakeOnLAN = Methods.create<MethodMessageResponse>( _SystemServices_METHOD_wakeonlan, "Wake on a PC through LAN", Permission::R_XR__R__() );
	mmWakeOnLAN->setMethod( this, (MethodMessageResponsePtr)&SystemServicesBundle::mmDoWakeOnLAN );

	setBundleState(Bundle::STATE_CREATED, false);
 }


SystemServicesBundle::~SystemServicesBundle() {
	Log(LOG_VERBOSE, " Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~SystemServicesBundle() : Destroying while Bundle is running! Trying to stop.." );
		bundleStop();
	 }
	if ( this->objects.count() != 0 ) {
		Log(LOG_VERBOSE, ":~SystemServicesBundle() : Freeing %d BObj..", this->objects.count() );
		this->objects.clear();
	 }
	Log(LOG_VERBOSE, ":~SystemServicesBundle() : Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT SystemServicesBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) ) return WRET_ERR_WRONG_STATE;

	WRESULT ret = loadObjects();

	setBundleState( Bundle::STATE_STARTED );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SystemServicesBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) ) return WRET_ERR_WRONG_STATE;

	WRESULT ret = saveObjects();

	setBundleState( Bundle::STATE_STOPPED );
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS

void SystemServicesBundle::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	BundleGeneric::busMessage( message, source );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT

////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// LOAD/SAVE OBJECTs

WRESULT SystemServicesBundle::loadObjects()
 {
	std::vector<SystemHost*> objects_load;
	WRESULT ret = PersistenceManager::loadObjects( objects_load, "XML", "$DATABASE/systemhosts.xml" );

	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":loadObjects() : FAILED#%d loading Database..", ret );
		Message* message = SystemError::createMessage( this, ret, SystemError::CRITICAL, "Failed loading Objects: " );
		BusCore.postMessage(message);
		return ret;
	 }
	if ( objects_load.size() == 0 ) {
		Log(LOG_WARNING, ":loadObjects() : No Items found in database.." );
		return WRET_OK;
	 }

	this->objects.transactionBeginWrite();
	// register objects
	std::vector<SystemHost*>::iterator it_load;
	for ( it_load=objects_load.begin(); it_load!=objects_load.end(); it_load++ ) {
		if ( *it_load == NULL ) continue;
		addObject_( *it_load );
	 }

	Properties.transactionBeginWrite();
	Properties.update( _SystemServices_KEY_count, this->objects.size() );
	Properties.transactionEnd();

	this->objects.transactionEnd();

	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////


WRESULT SystemServicesBundle::addObject_( SystemHost* sysHost )
 {
	if ( sysHost == NULL ) return WRET_ERR_PARAM;

	sysHost->getURI().setKernelLocal();
	sysHost->getURI().setParent( this->getURI() );
	sysHost->getURI().setName( sysHost->getName() );
	sysHost->getURI().registerGlobal();

	this->objects.set( sysHost->getName(), sysHost );

	return WRET_OK;
 }

WRESULT SystemServicesBundle::removeObject_( SystemHost* sysHost )
 {
	if ( sysHost == NULL ) return WRET_ERR_PARAM;

	if ( sysHost->getURI().isGlobal() )
		sysHost->getURI().unRegisterGlobal();

	this->objects.erase(sysHost->getName(), true);

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SystemServicesBundle::saveObjects()
 {
	this->objects.transactionBeginRead();
	WRESULT ret = PersistenceManager::saveObjects( this->objects.getMap(), "XML", "$DATABASE/systemhosts2.xml" );
	this->objects.transactionEnd();
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////// LOAD/SAVE OBJECTs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
/*
Response* SystemServicesBundle::mmDoReloadBObj( const Request* request )
 {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().getData();
	Log(LOG_VERBOSE, ":mmDoReloadBObj(%s)", option.c_str() );

	this->objects.free();
	WRESULT ret = loadObjects();
 
	return request->replyResponse(ret);
 }
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* SystemServicesBundle::mmDoWakeOnLAN( const Message* requestMessage )
 {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();

	if ( request->getDataClassName() == Variant::className() ) {
		string syshostName = request->getArgument().toString();
		Log(LOG_VERBOSE, ":mmDoWakeOnLAN(%s)", syshostName.c_str() );

		this->objects.transactionBeginRead();
		SystemHost* sysHost = this->objects.find(syshostName);
		if ( sysHost == NULL ) {
			return request->replyResponse(WRET_ERR_PARAM, "SystemHost not found!");
			this->objects.transactionEnd();
		 }
		std::string ip = NetworkUtilities::getBroadcastAddress(sysHost->getIP()) + ":9";
		WRESULT ret = do_wakeonlan( ip, sysHost->getMAC() );
		this->objects.transactionEnd();
		if ( WSUCCEEDED(ret) )
			return request->replyResponse(WRET_OK, "Magic Packet sent!");
		return request->replyResponse(ret);
	 }
	else if ( request->getDataClassName() == List::className() ) {
		Variant dMac;
		Variant dIP;
		request->extractArguments(dMac, dIP);
		if ( !dMac.isStringNotEmpty() )
			return request->replyResponse(WRET_ERR_PARAM, "Invalid parameters");
		if ( !dIP.isStringNotEmpty() )
			dIP.set("192.168.0.255:9");
		WRESULT ret = do_wakeonlan( dIP.asString(), dMac.asString() );
		if ( WSUCCEEDED(ret) )
			return request->replyResponse(WRET_OK, "Magic Packet sent!");
		return request->replyResponse(ret);
	 }
	return request->replyResponse(WRET_OK, "Magic Packet sent!");
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////







//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace services
}; // namespace wosh
